package com.mspbots.sync.wise;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.mspbots.api.DataNotFoundException;
import com.mspbots.api.RestMessageObject;
import com.mspbots.sync.BaseGenericService;
import com.mspbots.sync.model.Tenant;
import com.mspbots.sync.tsheets.SheetsProperties;
import com.mspbots.sync.tsheets.SheetsSyncService;
import com.mspbots.sync.wise.client.ClientProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * ScheduledAsyncService
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/6/19
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class WiseSyncScheduled extends BaseGenericService {

    private final WiseSyncService wiseSyncService;
    private final SheetsSyncService sheetsSyncService;

    private Flux<Tenant> onGetTenants() {
        return this.coreClient.getFlux("tenant/active", null, Tenant.class);
    }

    @EventListener(ApplicationReadyEvent.class)
    public void autoManualSync() {
        this.onGetTenants()
                .flatMap(tenant -> this.manualSync(tenant.getId(), LocalDateTime.now().minusHours(1)))
                .subscribe(success -> log.info("Manual sync  is success. msg :{}", success),
                        err -> log.error("Manual sync is error.msg: {}", JSONUtil.toJsonStr(err)));
    }

    public Flux<Object> manualSync(Integer tenantId, LocalDateTime beginTime) {
        this.recordRedisValue("sync-ticket-entries-" + tenantId, beginTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        this.recordRedisValue("sync-time-entries-" + tenantId, beginTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        this.recordRedisValue("sync-wise-users-" + tenantId, beginTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        this.recordRedisValue("sync-schedule-entries-" + tenantId, beginTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        this.recordRedisValue("sync-contacts-" + tenantId, beginTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        this.recordRedisValue("sync-time-sheet-" + tenantId, beginTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

        log.debug("msp.trigger.event,tenant {}", tenantId);
        Object triggerEvent = Map.of("tenantId", tenantId, "eventName", "InitCwConfig",
                "scope", "System", "payload", objectMapper.createObjectNode().put("id", tenantId));
        this.messagingTemplate.convertAndSend("msp.trigger.event", "", triggerEvent);
        log.info("cw msp.trigger.event System [{}] ", this.objectMapper.convertValue(triggerEvent, JsonNode.class));

        return this.wiseSyncService.manualSync(tenantId)
                .mergeWith(this.sheetsSyncService.manualSync(tenantId)
                        .onErrorReturn(RestMessageObject.ok("Manual tsheet configuration is empty.")));
    }

    public Flux<Object> manualSyncTickets(Integer tenantId) {
        this.wiseSyncService.manualSyncTickets(tenantId);
        return Flux.just(RestMessageObject.ok("Manual sync Tickets success"));
    }

    /**
     * Scheduled sync cw time zone on on 12 H.
     */
//    @Scheduled(cron = "0 0 8 * * ?")
    @Scheduled(cron = "${mspbots.fixedDelay.syncUser}")
    public void syncUser() {
        Optional<List<Tenant>> tenantsOpt = this.onGetTenants().collectList().blockOptional(Duration.ofSeconds(5));
        if (tenantsOpt.isPresent()) {
            for (Tenant tenant : tenantsOpt.get()) {
                Optional<ClientProperties> wiseClientPropertiesOpt =
                        ClientProperties.build(tenant.getConfiguration()).blockOptional();
                if (wiseClientPropertiesOpt.isPresent()) {
                    ClientProperties clientProperties = wiseClientPropertiesOpt.get();
                    log.info("Auto sync users start. msg :{}", tenant.getId());
                    //this.wiseSyncService.autoSyncBoard(clientProperties);
                    //this.wiseSyncService.autoSyncPriority(clientProperties);
                    this.wiseSyncService.autoSyncAgreements(clientProperties);
                    this.wiseSyncService.autoSyncContacts(clientProperties);
                    this.wiseSyncService.autoSyncSkills(clientProperties);
                    this.wiseSyncService.autoSyncCompanies(clientProperties);
                    this.wiseSyncService.autoSyncSlas(clientProperties);
                    this.wiseSyncService.autoSyncSubdata(clientProperties);
                    this.wiseSyncService.autoSyncProjectPhase(clientProperties);
                }

                Optional<SheetsProperties> tsheetPropertiesOpt = SheetsProperties.build(tenant.getConfiguration()).blockOptional();
                if (tsheetPropertiesOpt.isPresent()) {
                    SheetsProperties sheetsProperties = tsheetPropertiesOpt.get();
                    log.info("Auto sync tsheet user start. msg: {}", tenant.getId());
                    this.sheetsSyncService.autoSyncUsers(sheetsProperties);
                }
            }
        }
    }

    /**
     * Scheduled sync cw data on 30 seconds.
     */
    @Scheduled(fixedDelayString = "${mspbots.fixedDelay.autoSyncTime}")
    public void autoSyncTime() {
        Optional<List<Tenant>> tenantsOpt = this.onGetTenants().collectList().blockOptional(Duration.ofSeconds(5));
        if (tenantsOpt.isPresent()) {
            for (Tenant tenant : tenantsOpt.get()) {
                Optional<ClientProperties> wiseClientPropertiesOpt =
                        ClientProperties.build(tenant.getConfiguration()).blockOptional();
                if (wiseClientPropertiesOpt.isPresent()) {
                    ClientProperties clientProperties = wiseClientPropertiesOpt.get();
                    log.info("Auto sync entries start : {}", clientProperties.getTenantId());
                    this.wiseSyncService.autoSyncTimeEntries(clientProperties);
                }
            }
        }

    }

    /**
     * Scheduled sync cw data on 60 * 5 seconds.
     */
    @Scheduled(fixedDelayString = "${mspbots.fixedDelay.autoSyncTicket}")
    public void autoSyncTicket() {
        Optional<List<Tenant>> tenantsOpt = this.onGetTenants().collectList().blockOptional(Duration.ofSeconds(5));
        if (tenantsOpt.isPresent()) {
            for (Tenant tenant : tenantsOpt.get()) {
                Optional<ClientProperties> wiseClientPropertiesOpt =
                        ClientProperties.build(tenant.getConfiguration()).blockOptional();
                if (wiseClientPropertiesOpt.isPresent()) {
                    ClientProperties clientProperties = wiseClientPropertiesOpt.get();
                    log.info("Auto sync ticket start : {}", clientProperties.getTenantId());
                    this.wiseSyncService.autoSyncTicketEntries(clientProperties);
                    this.wiseSyncService.autoSyncUsers(clientProperties);
                    this.wiseSyncService.autoSyncScheduleEntries(clientProperties);
                }
            }
        }

    }

    /**
     * Scheduled sync cw data on 60 * 5 seconds.
     */
    @Scheduled(fixedDelayString = "${mspbots.fixedDelay.autoSyncThees}")
    public void autoSyncThees() {
        Optional<List<Tenant>> tenantsOpt = this.onGetTenants().collectList().blockOptional(Duration.ofSeconds(5));
        if (tenantsOpt.isPresent()) {
            for (Tenant tenant : tenantsOpt.get()) {
                SheetsProperties.build(tenant.getConfiguration())
                        .switchIfEmpty(Mono.defer(() -> Mono.error(new DataNotFoundException("This tenant " + tenant.getId()
                                + " tsheet configuration is empty."))))
                        .subscribe(this.sheetsSyncService::autoSyncTimeSheets,
                                ex -> log.error("Auto sync tsheet time is error. msg: {}", ex.getMessage()));
            }
        }
    }

}
